﻿namespace PorteringManagement.RouteEngine
{
    using System.Collections.Generic;
    using System.Linq;

    public class Engine
    {        
        public Engine()
        {
            this.Connections = new List<Connection>();
            this.Locations = new List<Location>();
        }

        public IList<Location> Locations { get; set; }

        public IList<Connection> Connections { get; set; }

        /// <summary>
        /// Calculates the shortest route to all the other locations
        /// </summary>
        /// <param name="startLocation"></param>
        /// <returns>List of all locations and their shortest route</returns>
        public Dictionary<Location, Route> CalculateMinCost(Location startLocation)
        {
            // Initialize a new empty route list.
            var shortestPaths = new Dictionary<Location, Route>();            
            
            // Initialize a new empty handled locations list.
            var handledLocations = new List<Location>();
            
            // Initialise the new routes. the constructor will set the route weight to int.max.
            foreach (var location in this.Locations)
            {
                shortestPaths.Add(location, new Route(location.Identifier));
            }

            // The startPosition has a weight 0.
            shortestPaths[startLocation].Cost = 0;            

            // If all locations are handled, stop the engine and return the result.
            while (handledLocations.Count != this.Locations.Count)
            {
                // Order the locations.
                var shortestLocations = from s in shortestPaths orderby s.Value.Cost select s.Key;              
                Location locationToProcess = null;

                // Search for the nearest location that is not handled.
                foreach (var location in shortestLocations)
                {
                    if (!handledLocations.Contains(location))
                    {
                        // If the cost equals int.max, there are no more possible connections to the remaining locations.
                        if (shortestPaths[location].Cost == int.MaxValue)
                        {
                            return shortestPaths;
                        }
                        
                        locationToProcess = location;
                        break;
                    }
                }

                // Select all connections where the startposition is the location to Process.
                var selectedConnections = from c in this.Connections where c.A == locationToProcess select c;

                // Iterate through all connections and search for a connection which is shorter.
                foreach (var conn in selectedConnections)
                {
                    if (shortestPaths[conn.B].Cost > conn.Weight + shortestPaths[conn.A].Cost)
                    {
                        shortestPaths[conn.B].Connections = shortestPaths[conn.A].Connections.ToList();
                        shortestPaths[conn.B].Connections.Add(conn);
                        shortestPaths[conn.B].Cost = conn.Weight + shortestPaths[conn.A].Cost;
                    }
                }

                // Add the location to the list of processed locations.
                handledLocations.Add(locationToProcess);
            }
            
            return shortestPaths;
        }
    }
}
